/*
 * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2012 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebViewPrivate.h"

#include "AboutData.h"
#include "BackForwardController.h"
#include "Chrome.h"
#include "ChromeClientManx.h"
#include "ContextMenuClientManx.h"
#include "DragClientManx.h"
#include "Editor.h"
#include "EditorClientManx.h"
#include "EmptyClients.h"
#include "EventHandler.h"
#include "FileChooser.h"
#include "FileSystem.h"
#include "FocusController.h"
#include "FormState.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameLoader.h"
#include "FrameLoaderClientManx.h"
#include "FrameSelection.h"
#include "FrameView.h"
#include "GCController.h"
#include "GraphicsContext.h"
#include "HTMLFormElement.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLTextAreaElement.h"
#include "HitTestResult.h"
#include "InspectorClientManx.h"
#include "InspectorController.h"
#include "Logging.h"
#include "Page.h"
#include "PlatformGestureEvent.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformMouseEvent.h"
#include "PlatformTouchEvent.h"
#include "PlatformTouchPoint.h"
#include "RenderBlock.h"
#include "ResourceRequest.h"
#include "ScriptController.h"
#include "ScriptValue.h"
#include "Scrollbar.h"
#include "Settings.h"
#include "SubstituteData.h"
#include "TextEncoding.h"
#include "TouchEvent.h"
#include "WebCString.h"
#include "WebKitVersion.h"
#include "WebViewHitTestResult.h"
#include "WebViewManagerPrivate.h"
#include <manx/TouchEvent.h>
#include <wtf/MainThread.h>
#include <wtf/StringExtras.h>

#if ENABLE(GEOLOCATION)
#include "GeoLocationClientManx.h"
#endif

#if ENABLE(DEVICE_ORIENTATION)
#include "DeviceMotionClientManx.h"
#include "DeviceOrientationClientManx.h"
#endif

#if OS(PSP2)
#include <sched.h>
#endif

#if ENABLE(MHTML)
#include "MHTMLArchive.h"
#endif

using WebCore::IntSize;
using WebCore::IntRect;

#define WEBVIEW_COMMAND_DATA_MAX    7

#define MANX_WEBKIT_VERSION_RRE "Mozilla/5.0 (Manx) AppleWebKit/"
#define MANX_WEBKIT_VERSION_SUF " (KHTML, like Gecko)"


static inline WebCore::PlatformEvent::Type webcoreMouseEvent(MouseEventType event)
{
    switch (event) {
    case MOUSE_EVENT_MOVED:     return WebCore::PlatformEvent::MouseMoved;
    case MOUSE_EVENT_PRESSED:   return WebCore::PlatformEvent::MousePressed;
    case MOUSE_EVENT_RELEASED:  return WebCore::PlatformEvent::MouseReleased;
    default:                    return WebCore::PlatformEvent::MouseMoved;
    }
}

static inline WebCore::MouseButton webcoreMouseButton(MouseButtonType button)
{
    switch (button) {
    case MOUSE_BUTTON_NONE:     return WebCore::NoButton;
    case MOUSE_BUTTON_LEFT:     return WebCore::LeftButton;
    case MOUSE_BUTTON_MIDDLE:   return WebCore::MiddleButton;
    case MOUSE_BUTTON_RIGHT:    return WebCore::RightButton;
    default:                    return WebCore::NoButton;
    }
}


namespace WebKit {

/* class WebViewPrivate */
WebViewPrivate::WebViewPrivate(WebViewClient& client)
    : m_client(client)
    , m_settings(*this)
    , m_popupMenu(0)
    , m_frame(0)
    , m_defaultLayoutWidth(1024)
    , m_viewSize()
    , m_textureWidth(0)
    , m_textureHeight(0)
#if !USE_TILED_BUFFER
    , m_textureStride(0)
    , m_textureBufferSize(0)
    , m_textureBuffer(0)
    , m_cairoContext(0)
    , m_cairoSurface(0)
#endif
    , m_textMagnifier(1.0f)
    , m_contentsWidth(0)
    , m_contentsHeight(0)
    , m_displayTimer(WebCore::RunLoop::current(), this, &WebViewPrivate::displayTimerFired)
#if USE_TILED_BUFFER
    , m_pool(0)
    , m_painter(0)
    , m_tiledBufferPainter(0)
#endif
    , m_overflowScrollDeltaX(0)
    , m_overflowScrollDeltaY(0)
    , m_overflowScrollTarget(0)
    , m_clipX(0)
    , m_clipY(0)
    , m_clipW(0)
    , m_clipH(0)
    , m_pending(false)
    , m_updatedClipRect(false)
    , m_imageKURL(WebCore::KURL())
{
    String format = String::format("%s%d.%d%s", MANX_WEBKIT_VERSION_RRE, WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, MANX_WEBKIT_VERSION_SUF);
    m_userAgent = format.utf8();
}

void WebViewPrivate::create(WebViewCreateParam& param)
{
    // initialize popup menu
    m_popupMenu = new WebViewPopupMenu(this);

    m_viewSize = IntSize(param.m_width, param.m_height);

#if USE_TILED_BUFFER
    m_pool = (tilebackend::PoolInterface*)param.m_pool;
    m_painter = (tilebackend::PainterInterface*)param.m_painter;
    m_tiledBufferPainter = WebCore::TiledBufferPainter::create(
        IntSize(param.m_width, param.m_height),
        m_pool,
        m_painter);
    if (!m_tiledBufferPainter)
        CRASH();
#endif

    // prepare texture buffer
#if USE_TILED_BUFFER
    m_textureWidth = param.m_width;
    m_textureHeight = param.m_height;
#else
    m_textureWidth = width;
    m_textureHeight = height;
    m_textureStride = width * 4;
    m_textureBufferSize = m_textureStride * m_textureHeight;
    m_textureBuffer = new unsigned char[m_textureBufferSize];

    // prepare cairo context
    m_cairoSurface = cairo_image_surface_create_for_data(m_textureBuffer,
        CAIRO_FORMAT_ARGB32,
        m_textureWidth,
        m_textureHeight,
        m_textureStride);
    if (!m_cairoSurface) {
        printf("WebView::Create: cairo_image_surface_create_for_data() failed!\n");
        CRASH();
    }

    m_cairoContext = cairo_create(m_cairoSurface);
    if (cairo_status(m_cairoContext) != CAIRO_STATUS_SUCCESS) {
        printf("WebView::Create: cairo_create() failed!\n");
        CRASH();
    }
#endif

#if USE(ACCELERATED_COMPOSITING)
    m_acceleratedCompositingContext = AcceleratedCompositingContext::create(this);
    if (!m_acceleratedCompositingContext)
        CRASH();
#endif

    // initialize webcore members
    WebCore::Page::PageClients pageClients;
    pageClients.chromeClient = new WebCore::ChromeClientManx(this);
    pageClients.contextMenuClient = new WebCore::ContextMenuClientManx();
    pageClients.editorClient = new WebCore::EditorClientManx(this);
    pageClients.dragClient = new WebCore::DragClientManx();
    pageClients.inspectorClient = new WebCore::InspectorClientManx(this);
    m_page = adoptPtr(new WebCore::Page(pageClients));

    m_page->setGroupName(String("manxWebKit"));
    initSettings();

#if ENABLE(GEOLOCATION)
    if (param.m_enableGeolocation)
        m_geolocationClient = adoptPtr(new WebCore::GeolocationClientManx(this));
    else
        m_geolocationClient = adoptPtr(new WebCore::GeolocationClientManxNullified(this));
    WebCore::provideGeolocationTo(m_page.get(), m_geolocationClient.get());
    m_geolocationClient->setController(WebCore::GeolocationController::from(m_page.get()));
#endif

#if ENABLE(DEVICE_ORIENTATION)
    WebCore::provideDeviceMotionTo(m_page.get(), new WebCore::DeviceMotionClientManx);
    WebCore::provideDeviceOrientationTo(m_page.get(), new WebCore::DeviceOrientationClientManx);
#endif

    WebCore::FrameLoaderClientManx* loaderClient = new WebCore::FrameLoaderClientManx(this);
    RefPtr<WebCore::Frame> frame = WebCore::Frame::create(m_page.get(), 0, loaderClient);
    m_frame = frame.get();
    loaderClient->setFrame(m_frame);

    m_page->mainFrame()->init();
    m_page->focusController()->setActive(true);
    m_page->focusController()->setFocused(true);
}

WebViewPrivate::~WebViewPrivate()
{
    destroy();
#if USE_TILED_BUFFER
    delete m_painter;
    delete m_pool;
#endif
}

void WebViewPrivate::destroy()
{
    // cleanup webcore members
    m_frame->loader()->stopForUserCancel();
    m_frame->loader()->detachFromParent();

#if !USE_TILED_BUFFER
    // cleanup cairo context
    cairo_surface_finish(m_cairoSurface);
    cairo_surface_destroy(m_cairoSurface);
    cairo_destroy(m_cairoContext);

    // cleanup texture buffer
    delete [] m_textureBuffer;
    m_textureBuffer = 0;
#endif

    // cleanup popup menu
    delete m_popupMenu;
    m_popupMenu = 0;

#if USE_TILED_BUFFER
    delete m_tiledBufferPainter;
    m_tiledBufferPainter = 0;
#endif
    m_page->inspectorController()->inspectorClient()->closeInspectorFrontend();
}

void WebViewPrivate::displayTimerFired()
{
    bool needsRepaint = true;

    // paint webview
    if (!m_frame->view()->isPainting() && m_frame->contentRenderer() && m_frame->view()) {
        m_frame->view()->updateLayoutAndStyleIfNeededRecursive();
#if USE_TILED_BUFFER
        needsRepaint = m_tiledBufferPainter->updateTiles(m_frame->view());
        client().didUpdateDrawing(0, 0);
#else
        WebCore::GraphicsContext ctx(m_cairoContext);
        m_frame->view()->paint(&ctx, IntRect(0, 0, m_textureWidth, m_textureHeight));
#if ENABLE(INSPECTOR)
        if (m_frame->page()->inspectorController()->highlightedNode())
            m_frame->page()->inspectorController()->drawHighlight(ctx);
#endif
        needsRepaint = false;
        client().didUpdateDrawing(m_textureBuffer, m_textureBufferSize);
#endif
    }
    if (needsRepaint)
        scheduleDisplay(1.0 / 60.0);
}

void WebViewPrivate::invalidate(const IntRect& updateRect, bool immediate)
{
#if USE(ACCELERATED_COMPOSITING)
    if (m_acceleratedCompositingContext->enabled()) {
        m_acceleratedCompositingContext->setNonCompositedContentsNeedDisplay(updateRect);
        return;
    }
#endif

    scheduleDisplay();
#if USE_TILED_BUFFER
    IntRect rect = updateRect;
    WebCore::FrameView* view = m_page->mainFrame()->view();
    if (view && !view->delegatesScrolling()) {
        // Correct location of updateRect because it is
        // in the visible content rect coordinate.
        IntRect actualVisibleRect = view->visibleContentRect();
        rect.moveBy(actualVisibleRect.location());
    }
    m_tiledBufferPainter->invalidate(rect, immediate);
#endif
}

void WebViewPrivate::drawRect(float x, float y, float w, float h, void* data, unsigned dataSize)
{
#if USE(ACCELERATED_COMPOSITING)
    if (m_acceleratedCompositingContext->enabled()) {
        m_acceleratedCompositingContext->patchAndSubmitDrawCommands(IntRect(x, y, w, h), (tilebackend::Target*)data);
        return;
    }
#endif

#if USE_TILED_BUFFER
    m_tiledBufferPainter->paint((tilebackend::PlatformSurface*)data, IntRect(x, y, w, h));
#endif
}

void WebViewPrivate::setClipRect(int x, int y, int w, int h, bool pending)
{
    bool needsRepaint = false;
    WebCore::Frame* frame = m_frame;
    IntRect actualVisibleRect = frame->view()->visibleContentRect();
    IntRect contentsRect(WebCore::IntPoint(), frame->view()->contentsSize());
    WebCore::IntPoint currentPosition = actualVisibleRect.location();
    WebCore::IntPoint newPosition(x, y);
    IntSize currentSize = actualVisibleRect.size();
    IntSize newSize(w, h);
    IntSize contentsSize = frame->view()->contentsSize();
    WebCore::IntPoint maximumOffset(contentsSize.width() - newSize.width(), contentsSize.height() - newSize.height());
    maximumOffset.clampNegativeToZero();
    newPosition = newPosition.shrunkTo(maximumOffset);
    newPosition.clampNegativeToZero();

    float scaleY = (float)m_viewSize.height() / newSize.height();

#if USE(ACCELERATED_COMPOSITING)
    if (m_acceleratedCompositingContext->enabled()) {
        IntRect newVisibleRect(newPosition, newSize);
        newVisibleRect.intersect(contentsRect);
        if (newVisibleRect != actualVisibleRect) {
            frame->view()->setFixedVisibleContentRect(newVisibleRect);
            frame->view()->updateLayoutAndStyleIfNeededRecursive();
            m_acceleratedCompositingContext->setActualVisibleRect(newVisibleRect);
        }
        return;
    }
#endif

#if USE_TILED_BUFFER
    // Check pinch-out action.
    if (pending) {
        // Return if action is pinch-in.
        if (scaleY >= m_tiledBufferPainter->contentsScale())
            return;

        // Quantize scale.
        const float granularity = 0.5;
        float newScale = scaleY;
        newScale = ((int)(newScale / granularity)) * granularity;
        if (newScale < granularity)
            newScale = granularity;

        scaleY = newScale;
    }

    if (scaleY != m_tiledBufferPainter->contentsScale()) {
        m_tiledBufferPainter->setContentsScale(scaleY);
        needsRepaint = true;
    }

    if (!m_tiledBufferPainter->tileCount())
        needsRepaint = true;
#endif
    if (currentPosition != newPosition || currentSize != newSize) {
        actualVisibleRect.setLocation(newPosition);
        actualVisibleRect.setSize(newSize);
        actualVisibleRect.intersect(contentsRect);
        frame->view()->setFixedVisibleContentRect(actualVisibleRect);
        needsRepaint = true;
    }
    if (needsRepaint)
        scheduleDisplay();
}

void WebViewPrivate::getClipRect(int* x, int* y, int* w, int* h)
{
    WebCore::Frame* frame = m_frame;
    IntRect actualVisibleRect = frame->view()->visibleContentRect();
    *x = actualVisibleRect.x();
    *y = actualVisibleRect.y();
    *w = actualVisibleRect.width();
    *h = actualVisibleRect.height();
}

void WebViewPrivate::sendMouseEvent(MouseEventType event, MouseButtonType button, int x, int y)
{
    WebCore::Frame* frame = m_page->mainFrame();
    WebCore::PlatformMouseEvent platformMouseEvent(webcoreMouseEvent(event), webcoreMouseButton(button), x, y);
    m_page->cursorNavigation()->setCursorPosition(platformMouseEvent);
    switch (event) {
    case MOUSE_EVENT_MOVED:
        frame->eventHandler()->mouseMoved(platformMouseEvent);
        break;
    case MOUSE_EVENT_PRESSED:
        frame->eventHandler()->handleMousePressEvent(platformMouseEvent);
        break;
    case MOUSE_EVENT_RELEASED:
        frame->eventHandler()->handleMouseReleaseEvent(platformMouseEvent);
        break;
    default:
        break;
    }
}

bool WebViewPrivate::sendKeyEvent(Manx::KeyboardEvent& event, KeyDefaultBehavior behavior)
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    WebCore::PlatformKeyboardEvent platformEvent(event);
    bool consumed = frame->eventHandler()->keyEvent(platformEvent);
    if (!consumed) {
        switch (behavior) {
        case KeyDefaultBehaviorNone:
            break;
        case KeyDefaultBehaviorScroll:
            // FIXME: Not implement yet.
            break;
        case KeyDefaultBehaviorCursorNavigation:
            WebCore::IntPoint newCursorPosition;
            bool moved;
            consumed = m_page->cursorNavigation()->handleKeyEvent(platformEvent, moved, newCursorPosition);
            if (moved)
                client().setCursorPosition(newCursorPosition);
        }
    }
    return consumed;
}

bool WebViewPrivate::sendTouchEvent(const Manx::TouchEvent* event)
{
    bool ret = false;
#if ENABLE(TOUCH_EVENTS)
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    if (frame->view()->delegatesScrolling()) {
        // If delegateScrolling is ON, touch points should be converted to
        // the contents coordinate at the responsibility of WebView.

        // Currently, touch points are scaled by app framework, so here
        // correct the scroll position. 
        IntSize offset = frame->view()->scrollOffset();

        if (m_viewSize.isEmpty())
            return ret;

        Manx::TouchEvent e;
        e.touchPointsLength = event->touchPointsLength;
        bool touchMove = false;
        for (int i = 0; i < e.touchPointsLength; ++i) {
            e.touchPoints[i] = event->touchPoints[i];
            e.touchPoints[i].position.x += offset.width();
            e.touchPoints[i].position.y += offset.height();
            touchMove |= (e.touchPoints[i].state == Manx::TouchPoint::StateMoved);
        }

        WebCore::PlatformTouchEvent touchEvent(e);
        ret = frame->eventHandler()->handleTouchEvent(touchEvent);
        if (touchMove && isNeedOverflowScroll())
            ret |= doOverflowScroll();
    } else {
        WebCore::PlatformTouchEvent touchEvent(*event);
        ret = frame->eventHandler()->handleTouchEvent(touchEvent);
    }
#endif
    return ret;
}

void WebViewPrivate::sendGestureEvent(unsigned type, int x, int y)
{
#if ENABLE(GESTURE_EVENTS)
    WebCore::Frame* frame = m_frame;
    WebCore::IntPoint point(x, y);
    point.moveBy(WebCore::IntPoint(frame->view()->scrollOffset()));
    WebCore::PlatformGestureEvent gestureEvent(WebCore::PlatformEvent::GestureTap, point, point, WTF::currentTime(), 0, 0, false, false, false, false);
    frame->eventHandler()->handleGestureEvent(gestureEvent);
#endif
}

void WebViewPrivate::loadURL(const char* url)
{
    WebCore::KURL kurl = WebCore::KURL(WebCore::KURL(), WTF::String::fromUTF8(url), WebCore::UTF8Encoding());
    if (kurl.protocol().isEmpty()) {
        if (WebCore::fileExists(url)) {
            kurl.setProtocol("file");
            kurl.setPath("//" + kurl.path());
        } else
            kurl = WebCore::KURL(WebCore::KURL(), "http://" + WTF::String::fromUTF8(url), WebCore::UTF8Encoding());
    }

    if (kurl.protocol() == "javascript")
        loadJavaScriptURL(kurl);
    else if (kurl.protocol() == "about")
        m_frame->loader()->load(WebCore::FrameLoadRequest(m_frame, WebCore::ResourceRequest(createAboudDataUri(kurl))));
    else
        m_frame->loader()->load(WebCore::FrameLoadRequest(m_frame, WebCore::ResourceRequest(kurl)));
}

void WebViewPrivate::loadHTML(const char *html, const char *baseUrl)
{
    // mimeType:texthtml, encoding:UTF-8
    loadData(html, strlen(html), "text/html", "UTF-8", baseUrl);
}

void WebViewPrivate::loadData(const char *data, unsigned long dataLen, const char *mimeType, const char *encoding, const char *baseUrl)
{
    const String& mimeTypeStr = mimeType;
    bool isMulti = equalIgnoringCase("multipart/related", mimeTypeStr);

    WTF::RefPtr<WebCore::SharedBuffer> sharedBuffer(WebCore::SharedBuffer::create(data, dataLen));
    WebCore::KURL baseKURL = WebCore::KURL(WebCore::KURL(WebCore::KURL(), WTF::String::fromUTF8(baseUrl), WebCore::UTF8Encoding()));

    if (isMulti) {
        // mhtml, create SharedBuffer from data.
        loadArchiveWithSharedBuffer(sharedBuffer, baseKURL);
    } else {
        // etc
        WebCore::ResourceRequest request(baseKURL);
        WebCore::SubstituteData substituteData(sharedBuffer.release(),
            WTF::String::fromUTF8(mimeType),
            (strlen(encoding) > 0) ? WTF::String::fromUTF8(encoding) : WTF::String::fromUTF8("ISO-8859-1"),
            WebCore::KURL(WebCore::KURL(), WTF::String()),
            WebCore::KURL(WebCore::KURL(), WTF::String()));
        m_frame->loader()->load(WebCore::FrameLoadRequest(m_frame, request, substituteData));
    }
}

void WebViewPrivate::loadArchive(const char *buf, unsigned long bufLen, const char *url)
{
    // create SharedBuffer from url.
    WTF::RefPtr<WebCore::SharedBuffer> sharedBuffer(WebCore::SharedBuffer::create(buf, bufLen));

    WebCore::KURL baseKURL = WebCore::KURL(WebCore::KURL(WebCore::KURL(), WTF::String::fromUTF8(url), WebCore::UTF8Encoding()));
    loadArchiveWithSharedBuffer(sharedBuffer, baseKURL);
}

void WebViewPrivate::loadArchiveWithSharedBuffer(WTF::RefPtr<WebCore::SharedBuffer> sharedBuffer, WebCore::KURL baseKURL)
{
#if ENABLE(MHTML)
    WTF::PassRefPtr<WebCore::MHTMLArchive> arc = WebCore::MHTMLArchive::create(baseKURL, sharedBuffer.get());

    if (arc)
        m_frame->loader()->loadArchive(arc);
#endif
}

void WebViewPrivate::reload()
{
    m_frame->loader()->reload();
}

void WebViewPrivate::stop()
{
    m_frame->loader()->stopForUserCancel();
}

bool WebViewPrivate::canGoForward()
{
    return m_page->backForward()->canGoBackOrForward(1);
}

void WebViewPrivate::goForward()
{
    m_page->goForward();
}

bool WebViewPrivate::canGoBack()
{
    return m_page->backForward()->canGoBackOrForward(-1);
}

void WebViewPrivate::goBack()
{
    m_page->goBack();
}

bool WebViewPrivate::canIncreaseTextSize() const
{
    return m_textMagnifier * TextSizeMultiplierRatio <= MaximumTextSizeMultiplier;
}

void WebViewPrivate::increaseTextSize()
{
    if (canIncreaseTextSize()) {
        m_textMagnifier = m_textMagnifier * TextSizeMultiplierRatio;
        m_frame->setTextZoomFactor(m_textMagnifier);
    }
}

bool WebViewPrivate::canDecreaseTextSize() const
{
    return m_textMagnifier / TextSizeMultiplierRatio >= MinimumTextSizeMultiplier;
}

void WebViewPrivate::decreaseTextSize()
{
    if (canDecreaseTextSize()) {
        m_textMagnifier = m_textMagnifier / TextSizeMultiplierRatio;
        m_frame->setTextZoomFactor(m_textMagnifier);
    }
}

void WebViewPrivate::resetTextSize()
{
    m_textMagnifier = 1.0;
    m_frame->setTextZoomFactor(m_textMagnifier);
}

void WebViewPrivate::setComposition(const char *compositionString, unsigned underlineFrom, unsigned underlineTo)
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    if (!frame || !frame->editor().canEdit())
        return;

    Vector<WebCore::CompositionUnderline> underlines;
    underlines.append(WebCore::CompositionUnderline(underlineFrom, underlineTo, WebCore::Color(0, 0, 0), false));
    frame->editor().setComposition(String::fromUTF8(compositionString), underlines, underlineTo, underlineTo);
    // FIXME: Always send composition event as key event (=229)
    frame->eventHandler()->keyEvent(WebCore::PlatformKeyboardEvent(Manx::KeyboardEvent(Manx::KeyboardEvent::KeyDown, 229, false, false, false, false, false)));
    frame->eventHandler()->keyEvent(WebCore::PlatformKeyboardEvent(Manx::KeyboardEvent(Manx::KeyboardEvent::KeyUp,   229, false, false, false, false, false)));
}

void WebViewPrivate::confirmComposition(const char *compositionString, unsigned caretIndex)
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    if (!frame || !frame->editor().canEdit())
        return;
    frame->editor().confirmComposition(String::fromUTF8(compositionString), caretIndex);
    // FIXME: Always send composition event as key event (=229)
    frame->eventHandler()->keyEvent(WebCore::PlatformKeyboardEvent(Manx::KeyboardEvent(Manx::KeyboardEvent::KeyDown, 229, false, false, false, false, false)));
    frame->eventHandler()->keyEvent(WebCore::PlatformKeyboardEvent(Manx::KeyboardEvent(Manx::KeyboardEvent::KeyUp,   229, false, false, false, false, false)));
}

void WebViewPrivate::confirmComposition()
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    if (!frame || !frame->editor().hasComposition())
        return;
    frame->editor().confirmComposition();
}

void WebViewPrivate::cancelComposition()
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    if (!frame || !frame->editor().canEdit())
        return;
    frame->editor().cancelComposition();
}

void WebViewPrivate::exitComposition()
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    WebCore::Element* elem = frame->document()->focusedElement();
    if (!elem)
        return;
    elem->blur();
}

void WebViewPrivate::clearText()
{
    setValueToFocusedNode(String(""));
}

void WebViewPrivate::setValueToFocusedNode(const String& value)
{
    WebCore::Frame* frame = m_page->focusController()->focusedOrMainFrame();
    if (!frame || !frame->editor().canEdit())
        return;

    if (frame->editor().hasComposition())
        frame->editor().cancelComposition();

    WebCore::Element* elem = frame->document()->focusedElement();
    if (!elem)
        return;
    if (elem->hasTagName(WebCore::HTMLNames::inputTag)) {
        WebCore::HTMLInputElement* element = static_cast<WebCore::HTMLInputElement*>(elem);
        element->setValue(value, WebCore::DispatchInputAndChangeEvent);
    } else if (elem->hasTagName(WebCore::HTMLNames::textareaTag)) {
        WebCore::HTMLTextAreaElement* element = static_cast<WebCore::HTMLTextAreaElement*>(elem);
        element->setValue(value);
        elem->dispatchInputEvent();
    } else {
        WebCore::ExceptionCode ec = 0;
        elem->setTextContent(value, ec);
        elem->dispatchInputEvent();
    }
}

void WebViewPrivate::setSelectedItemsPopupMenu(int index, bool isEnd)
{
    if (index < 0 && isEnd)
        m_popupMenu->hide(true);
    else if (index < 0 && !isEnd)
        m_popupMenu->setValue(-1);
    else if (index >= 0 && isEnd)
        m_popupMenu->hide(false);
    else
        m_popupMenu->setValue(index); // (index >= 0 && !isEnd)
}

void WebViewPrivate::getItemPopupMenu(int index)
{
    m_popupMenu->getItem(index);
}

void WebViewPrivate::initSettings()
{
    WebCore::Settings* settings = m_page->settings();
    settings->setScriptEnabled(true);
    settings->setFrameFlatteningEnabled(true);
    settings->setDefaultFontSize(16);
    settings->setDefaultFixedFontSize(13);
    settings->setSerifFontFamily("Times New Roman");
    settings->setFixedFontFamily("Courier New");
    settings->setSansSerifFontFamily("Arial");
    settings->setStandardFontFamily("Arial");
    settings->setSpatialNavigationEnabled(false);
#if 0
    settings->setMemoryInfoEnabled(true);
#endif
    settings->setLoadsImagesAutomatically(true);
    settings->setLocalStorageEnabled(true);
    settings->setUsesEncodingDetector(true);
    settings->setCookieEnabled(true);
    settings->setAllowScriptsToCloseWindows(true);
    settings->setCursorNavigationEnabled(true);
    settings->setDeveloperExtrasEnabled(false);

#if USE(ACCELERATED_COMPOSITING)
    settings->setAcceleratedCompositingEnabled(true);
    settings->setAcceleratedCompositingFor3DTransformsEnabled(true);
    settings->setAcceleratedCompositingForAnimationEnabled(true);
    settings->setAcceleratedCompositingForCanvasEnabled(true);
    settings->setAcceleratedCompositingForFixedPositionEnabled(true);
    settings->setAcceleratedCompositingForOverflowScrollEnabled(true);
    settings->setAcceleratedCompositingForPluginsEnabled(true);
    settings->setAcceleratedCompositingForScrollableFramesEnabled(true);
    settings->setAcceleratedCompositingForVideoEnabled(true);

    settings->setForceCompositingMode(false);
    settings->setShowRepaintCounter(false);
#endif
}

void WebViewPrivate::setUserAgent(const char* userAgent)
{
    WTF::MutexLocker locker(m_mutex);
    m_userAgent = CString(userAgent);
}

const char* WebViewPrivate::userAgent()
{
    WTF::MutexLocker locker(m_mutex);
    return m_userAgent.data();
}

void WebViewPrivate::hitTest(Manx::Point& hitPoint, WebViewHitTestResult& result, bool& elementForm)
{
    WebCore::LayoutPoint point(hitPoint.x, hitPoint.y);

    WebCore::HitTestResult hitTestResult = m_frame->eventHandler()->hitTestResultAtPoint(point, false);

    //
    // TODO: Construct WebViewHitTestResult here
    //
    WebCore::KURL imageKURL = hitTestResult.absoluteImageURL();
    result.setImageUrl(imageKURL);
    result.setImageUrlLength(imageKURL.string().length());

    WebCore::KURL linkKURL = hitTestResult.absoluteLinkURL();
    result.setLinkUrl(linkKURL);
    result.setLinkUrlLength(linkKURL.string().length());

    WebCore::Element* element = hitTestResult.URLElement();
    WebCore::Node* node = hitTestResult.innerNode();

    bool canScroll = false;
    m_overflowScrollTarget = 0;
    WebCore::RenderBox* renderBox = node ? (node->renderer() ? node->renderer()->enclosingBox() : 0) : 0;
    while (renderBox && !renderBox->isBody()) {
        if (renderBox->canBeScrolledAndHasScrollableArea()) {
            canScroll = renderBox->scrollsOverflow();
            if (canScroll)
                m_overflowScrollTarget = renderBox->node();
            break;
        }
        if (renderBox->containingBlock() && !renderBox->containingBlock()->isRenderView())
            renderBox = (WebCore::RenderBox*)renderBox->containingBlock();
        else 
            break;
    }
    m_canScroll = canScroll;
    result.setCanScroll(canScroll);
    resetOverflowScroll();

    if (node) {
        WebCore::LayoutRect rect = node->boundingBox();
        if (element && !linkKURL.isEmpty() && imageKURL.isEmpty()) {
            while (node && !node->hasTagName(WebCore::HTMLNames::aTag))
                node = node->parentNode();
            if (!node)
                node = hitTestResult.innerNode();
            else {
                rect = node->boundingBox();
                rect.setWidth(element->screenRect().width());
                rect.setHeight(element->screenRect().height());
            }
        }
        result.setRect(node->document()->frame()->view()->convertToContainingWindow(enclosedIntRect(rect)));

        WebCore::HTMLInputElement* input = node->toInputElement();
        if (input) {
            elementForm |= input->isRadioButton();
            elementForm |= input->isCheckbox();
        }
    }

    result.setPoint(WebCore::IntPoint(hitPoint.x, hitPoint.y));

    m_imageKURL = hitTestResult.absoluteImageURL();
    m_imageRef = hitTestResult.image();
    if (m_imageRef) {
        int dataSize = 0;
        if (m_imageRef->data())
            dataSize = m_imageRef->data()->size();

        String file = m_imageRef->filenameExtension();
        if (file == "jpg")
            file = "jpeg";
        file = "image/" + file;
        result.setFormat(file.utf8().data());
        result.setFormatLength(file.length());
        result.setImageSize(dataSize);
    }
}

void WebViewPrivate::setUploadFile(const char* path, const char* name)
{
    WebCore::ChromeClientManx* chromeClient = (WebCore::ChromeClientManx*)m_page->chrome().client();
    chromeClient->setUpload(path);
}

void WebViewPrivate::releaseTiles()
{
#if USE_TILED_BUFFER
    if (m_tiledBufferPainter)
        m_tiledBufferPainter->clear();
#endif
}
void WebViewPrivate::overflowScroll(int deltaX, int deltaY)
{
    WTF::MutexLocker locker(m_overflowScrollMutex);
    m_overflowScrollDeltaX += deltaX;
    m_overflowScrollDeltaY += deltaY;
}

bool WebViewPrivate::isNeedOverflowScroll()
{
    bool needScroll = false;
    if (m_canScroll && m_overflowScrollTarget && m_overflowScrollTarget->renderBox())
        needScroll = true;
    else
        m_overflowScrollTarget = 0;
    return needScroll;
}

void WebViewPrivate::resetOverflowScroll(int* previousDeltaX, int* previousDeltaY)
{
    WTF::MutexLocker locker(m_overflowScrollMutex);
    if (previousDeltaX)
        *previousDeltaX = m_overflowScrollDeltaX;
    if (previousDeltaY)
        *previousDeltaY = m_overflowScrollDeltaY;
    
    m_overflowScrollDeltaX = 0;
    m_overflowScrollDeltaY = 0;
}

bool WebViewPrivate::doOverflowScroll()
{
    bool prevent = true;
    int overflowScrollDeltaX = 0;
    int overflowScrollDeltaY = 0;

    resetOverflowScroll(&overflowScrollDeltaX, &overflowScrollDeltaY);

    if (m_overflowScrollTarget && m_overflowScrollTarget->renderBox() && (overflowScrollDeltaX || overflowScrollDeltaY)) {
        bool scrollDone = false;
        if (overflowScrollDeltaX)
            scrollDone |= m_overflowScrollTarget->renderBox()->scroll(overflowScrollDeltaX > 0 ? WebCore::ScrollLeft : WebCore::ScrollRight, WebCore::ScrollByPixel, abs((float)overflowScrollDeltaX));
        if (overflowScrollDeltaY)
            scrollDone |= m_overflowScrollTarget->renderBox()->scroll(overflowScrollDeltaY > 0 ? WebCore::ScrollUp : WebCore::ScrollDown, WebCore::ScrollByPixel, abs((float)overflowScrollDeltaY));
        prevent &= scrollDone;
    }
    return prevent;
}

void WebViewPrivate::getInputMethodInfo(InputMethodInfo& info)
{
    WebCore::Element* node = m_page->focusController()->focusedOrMainFrame()->document()->focusedElement();

    info.type = FocusedNodeUnknown;
    info.title = String();
    info.value = String();
    info.isMultiple = false;

    if (!node)
        return;
    if (node->matchesReadOnlyPseudoClass())
        return;
    
    if (node->hasTagName(WebCore::HTMLNames::textareaTag)) {
        WebCore::HTMLTextAreaElement* textarea = static_cast<WebCore::HTMLTextAreaElement*>(node);
        info.type = FocusedNodeText;
        info.title = textarea->title();
        info.value = textarea->value();
        info.isMultiple = true;
    } else if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
        WebCore::HTMLInputElement* input = static_cast<WebCore::HTMLInputElement*>(node);
        if (input->isTextField()) {
            if (input->isPasswordField())
                info.type = FocusedNodePassword;
            else if (input->isNumberField())
                info.type = FocusedNodeNumber;
            else if (input->isTelephoneField())
                info.type = FocusedNodeTel;
            else if (input->isEmailField())
                info.type = FocusedNodeEMail;
            else if (input->isURLField())
                info.type = FocusedNodeURL;
            else if (input->isSearchField())
                info.type = FocusedNodeSearch;
            else
                info.type = FocusedNodeText;
            info.title = input->title();
            info.value = input->value();
            info.isMultiple = input->multiple();
        }
    } else if (node->isContentEditable()) {
        info.type = FocusedNodeText;
        info.value = node->textContent(true);
        info.isMultiple = true;
    }

    if (info.type != FocusedNodeUnknown)
        info.rect = node->document()->frame()->view()->convertToContainingWindow(enclosedIntRect(node->boundingBox()));
}

void WebViewPrivate::setCaretVisible(bool visible)
{
    m_page->focusController()->focusedOrMainFrame()->selection()->setCaretVisible(visible);
}

bool WebViewPrivate::isProcessingUserGesture()
{
    return WebCore::ScriptController::processingUserGesture();
}

void WebViewPrivate::setGeolocationPermission(const char* origin, bool isAllowed)
{
#if ENABLE(GEOLOCATION)
    m_geolocationClient->setGeolocationPermission(origin, isAllowed);
#endif
}

void WebViewPrivate::updateGeolocationPosition(const WebViewGeolocationPosition* position)
{
#if ENABLE(GEOLOCATION)
    m_geolocationClient->updateGeolocationPosition(position);
#endif
}

bool WebViewPrivate::updateClipRect(int x, int y, int w, int h, bool pending)
{
    WTF::MutexLocker lock(m_mutex);
    bool needPushCommand = false;
    m_clipX = x;
    m_clipY = y;
    m_clipW = w;
    m_clipH = h;
    m_pending = pending;
    if (!m_updatedClipRect) {
        m_updatedClipRect = true;
        needPushCommand = true;
    }
    return needPushCommand;
}

void WebViewPrivate::applyClipRect()
{
    WTF::MutexLocker lock(m_mutex);
    if (m_updatedClipRect) {
        setClipRect(m_clipX, m_clipY, m_clipW, m_clipH, m_pending);
        m_updatedClipRect = false;
    }
}

void WebViewPrivate::setDefaultLayoutWidth(int width)
{
    m_defaultLayoutWidth = width;
}

void WebViewPrivate::loadJavaScriptURL(const WebCore::KURL& url)
{
    m_frame->script()->executeIfJavaScriptURL(url, WebCore::DoNotReplaceDocumentIfJavaScriptURL);
}

bool WebViewPrivate::getImageInfoHit(WebString& url, const char*& data, unsigned& size)
{
    if (!m_imageRef)
        return false;

    url = m_imageKURL.string();
    size = m_imageRef->data()->size();
    data = m_imageRef->data()->data();
    return true;
}

void WebViewPrivate::didCommitLoad()
{
    m_imageRef = 0;
    m_canScroll = false;
    m_overflowScrollTarget = 0;
}

void WebViewPrivate::scheduleDisplay(double delay)
{
    m_displayTimer.startOneShot(delay);
}

WebCore::Page* corePage(WebView* webView)
{
    if (!webView)
        return 0;

    WebViewPrivate* priv = reinterpret_cast<WebViewPrivate*>(webView);
    return priv ? priv->m_page.get() : 0;
}

} // namespace WebKit

